﻿namespace Sai.Core.Introspection
{
	using System.Collections.Generic;
	using System.Collections.ObjectModel;

	public abstract class ReferenceTypeDeclaration 
	{
		private ObsoleteMarker obsolete;

		private List<TypeName> implementingInterfaces = new List<TypeName>();

		private MethodDeclarationCollection methods = new MethodDeclarationCollection();

		private PropertyDeclarationCollection properties = new PropertyDeclarationCollection();

		private EventDeclarationCollection events = new EventDeclarationCollection();

		protected ReferenceTypeDeclaration()
		{
		}

		protected ReferenceTypeDeclaration(TypeName typeName)
			: this()
		{
			this.TypeName = typeName;
		}

		protected ReferenceTypeDeclaration(TypeName typeName, ObsoleteMarker marker)
			: this()
		{
			this.TypeName = typeName;
			this.obsolete = new ObsoleteMarker
			{
				IsEnabled = marker.IsEnabled,
				Reason = marker.Reason
			};
		}

		public TypeName TypeName { get; set; }

		public bool IsObsolete
		{
			get
			{
				if (this.obsolete == null)
				{
					return false;
				}

				return this.obsolete.IsEnabled;
			}
		}

		public string ObsoleteReason
		{
			get
			{
				if (this.obsolete != null
					&& !string.IsNullOrEmpty(this.obsolete.Reason))
				{
					return this.obsolete.Reason;
				}

				return string.Empty;
			}
		}

		public ReadOnlyCollection<TypeName> Interfaces
		{
			get
			{
				return new ReadOnlyCollection<TypeName>(this.implementingInterfaces);
			}
		}

		public ReadOnlyCollection<PropertyDeclaration> Properties
		{
			get
			{
				return new ReadOnlyCollection<PropertyDeclaration>(this.properties);
			}
		}

		public MethodDeclarationCollection Methods
		{
			get
			{
				return this.methods;
			}
		}

        public bool HasAnyOverloadedMethods
        {
            get
            {
                int allMethods = this.methods.Count;
                int distinctMethods = this.methods.DistinctMethods.Count;

                return allMethods != distinctMethods;
            }
        }

        public bool HasOverloadsFor(string methodName)
        {
            return this.methods.OverloadsFor(methodName).Count > 1;
        }

        public MethodDeclarationCollection NonOverloadedMethods
        {
            get
            {
                return this.methods.NonOverloadedMethods;
            }
        }

        public IList<string> OverloadedMethods
        {
            get
            {
                return this.methods.OverloadedMethods;
            }
        }

        public ReadOnlyCollection<string> DistinctMethods
        {
            get
            {
                return new ReadOnlyCollection<string>(this.methods.DistinctMethods);
            }
        }

		public ReadOnlyCollection<EventDeclaration> Events
		{
			get
			{
				return new ReadOnlyCollection<EventDeclaration>(this.events);
			}
		}

		public static bool operator ==(ReferenceTypeDeclaration a, ReferenceTypeDeclaration b)
		{
			if (object.ReferenceEquals(a, b))
			{
				return true;
			}

			if (((object)a == null) || ((object)b == null))
			{
				return false;
			}

			return a.TypeName == b.TypeName;
		}

		public static bool operator !=(ReferenceTypeDeclaration a, ReferenceTypeDeclaration b)
		{
			return !(a == b);
		}

		public override string ToString()
		{
			return this.TypeName.ToString();
		}

		public override int GetHashCode()
		{
			return this.TypeName.GetHashCode();
		}

		public void Implementing(TypeName interfaceType)
		{
			this.implementingInterfaces.Add(interfaceType);
		}
		
		// indexers...
		// need to go here too.
		public void Add(EventDeclaration eventInstance)
		{
			this.events.Add(eventInstance);
		}

		public void Add(PropertyDeclaration propertyInstance)
		{
			this.properties.Add(propertyInstance);
		}

		public void Add(MethodDeclaration methodInstance)
		{
			this.methods.Add(methodInstance);
		}

		public bool Equals(ITypeDeclaration other)
		{
			if ((object)other == null)
			{
				return false;
			}

			return this.TypeName.FullName == other.TypeName.FullName;
		}

		public override bool Equals(object obj)
		{
			if (obj == null)
			{
				return false;
			}

			ReferenceTypeDeclaration refDeclaration = obj as ReferenceTypeDeclaration;

			if ((object)refDeclaration == null)
			{
				return false;
			}

			return this.TypeName == refDeclaration.TypeName;
		}
 	}
}
